/*
jQuery.ganttView v.0.8.8
Copyright (c) 2010 JC Grubbs - jc.grubbs@devmynd.com
MIT License Applies
2019.7.22 修改，扩展按甘特图显示级别，支持按月显示，参数配置：scale： ["hours", "days", "weeks", "months"];
2018.4.1 by Ben
传入json的时间参数为字符串或毫秒整形值，读入后转换为date类型
html中元素
<div data-options="region:'center',border:false">             
            <div id="ganttChart" data-options="fit:true,border:false"></div> //gantt图           
            <div id="pp" class="easyui-pagination" style="border:1px solid #ccc;"></div>//分页控制
            <br />
            <div id="eventMessage"></div>//消息
        </div>
*/

/*
Options
-----------------
showWeekends: boolean
data: object //直接加载数据，格式见data.js
paradata: ,//ajax附加参数:{ name: "John", time: "2pm" }
cellWidth: number
cellHeight: number
slideWidth: number
dataUrl: string
behavior: {
	clickable: boolean,    
	draggable: boolean,
	resizable: boolean,
	onClick: function,
	onDrag: function,
	onResize: function
}

json格式：
code：项目编号
name：项目名称
mgr：管理员
desc：描述
{
name：节点名称
exec：执行人
time：完成时间
start0：end0：color0计划时间进度条起止时间、颜色
start,end，color实际时间起止，颜色
}
*/

(function (jQuery) {
    jQuery.fn.ganttView = function () {
        var args = Array.prototype.slice.call(arguments);
        //一个参数表示建立对象
        if (args.length == 1 && typeof (args[0]) == "object") {
            build.call(this, args[0]);
        }
        //两个参数表示执行函数
        if (args.length == 2 && typeof (args[0]) == "string") {
            handleMethod.call(this, args[0], args[1]);        }
    };

    function build(options) {
		var scales = ["hours", "days", "weeks", "months"];
        var els = this;
		//初始化默认参数；单元格宽度、高度等
        var defaults = {
			scale: "days",
            showWeekends: true,
			pagination:true,
            cellWidth: 21,
            cellHeight: 31,
            slideWidth: 400,
            vHeaderWidth: 100,
            afterload: function () { },
            behavior: {
                clickable: true,
                draggable: false,
                resizable: true
            }
        };

        var opts = jQuery.extend(true, defaults, options);
		//按js变量加载数据
        if (opts.data) {
            build();
        } else if (opts.dataUrl) {//ajax加载数据
            jQuery.getJSON(opts.dataUrl, opts.paradata, function (data) {
                opts.data = data.rows;
			 //json对象中日期字符串转换为日期对象,当前使用整形数字或时间字符串'2017-01-04',
			for(var i=0;i<opts.data.length;i++){ 			
				var plan=opts.data[i].series;
				for(var j=0;j<plan.length;j++){ 
				//alert("start:"+plan[j].start+",end:"+plan[j].end); 
					//var ms1 = parseInt(plan[j].start.replace(/\D/igm, ""));
					var reg = /[\u4E00-\u9FA5]/g;//检测汉字，新版本.net输出日期字符串带有“星期几”，js转换时不兼容
					//var result = title.replace(reg, '');
					var ms1 = plan[j].start;
				   // var day1 = new Date(ms1);
					if (ms1.length == 0) { opts.data[i].series[j].start = new Date(); } else { opts.data[i].series[j].start = new Date(ms1.replace(reg, '')); }
					var ms2=plan[j].end;			
					if (ms2.length == 0) { opts.data[i].series[j].end = new Date(); } else { opts.data[i].series[j].end = new Date(ms2.replace(reg, '')); }			  
				}			   
			} 
               if (opts.afterload) {
                   opts.afterload("一共多少页？" + data.total);
               	}			
               build();//生成甘特图
			   
                //绑定菜单
               $('.ganttview-vtheader').bind('contextmenu', function (e) {
                   e.preventDefault();
                   $('#menu_display').menu('show', {left:e.pageX,top:e.pageY});

               })
			});
        }

        function build() {
            var minDays = Math.floor((opts.slideWidth / opts.cellWidth) + 5);//最小显示日期数
            var startEnd = DateUtils.getBoundaryDatesFromData(opts.data, minDays);
            opts.start = startEnd[0];//计算甘特图的起止日期范围
            opts.end = startEnd[1];
 
            els.each(function () {//一般只有一个元素
 
                var container = jQuery(this);
				//加入分页栏<div id='pp'></div> <!-- <div id="pp" class="easyui-pagination" style="border:1px solid #ccc;"></div> -->
				// if(opts.pagination){					
				// 	var div= jQuery("<div id=’pp'>", { "class": "easyui-pagination" });
				// 	 container.append(div);
				// }				
				
                var div = jQuery("<div>", { "class": "ganttview" });
                new Chart(div, opts).render();
                container.append(div);

                var w = jQuery("div.ganttview-vtheader", container).outerWidth() +
					jQuery("div.ganttview-slide-container", container).outerWidth();
                container.css("width", (w + 2) + "px");
                new Behavior(container, opts).apply();
            });
        }
    }

    function handleMethod(method, value) {
        if (method == "setSlideWidth") {
            var div = $("div.ganttview", this);
            div.each(function () {
                var vtWidth = $("div.ganttview-vtheader", div).outerWidth();
                $(div).width(vtWidth + value + 1);
                $("div.ganttview-slide-container", this).width(value);
            });
        }
        if (method == "destroy") { $("div.ganttview", this).empty(); }
    }

    var Chart = function (div, opts) {

        function render() {
            addVtHeader(div, opts.data, opts.cellHeight);

            var slideDiv = jQuery("<div>", {
                "class": "ganttview-slide-container",
                "css": { "width": opts.slideWidth + "px" }
            });

            dates = getDates(opts.start, opts.end);
            addHzHeader(slideDiv, dates, opts.cellWidth);
            addGrid(slideDiv, opts.data, dates, opts.cellWidth, opts.showWeekends);
            addBlockContainers(slideDiv, opts.data);
            addBlocks(slideDiv, opts.data, opts.cellWidth, opts.start);
            div.append(slideDiv);
            applyLastClass(div.parent());
        }

        //var monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
		var monthNames = ["01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12"];
        // Creates a 3 dimensional array [year][month][day] of every day 
        // between the given start and end dates
        function getDates(start, end) {
            var dates = [];
            dates[start.getFullYear()] = [];
            dates[start.getFullYear()][start.getMonth()] = [start]
            var last = start;
            while (last.compareTo(end) == -1) {
                var next = last.clone().addDays(1);
                if (!dates[next.getFullYear()]) { dates[next.getFullYear()] = []; }
                if (!dates[next.getFullYear()][next.getMonth()]) {
                    dates[next.getFullYear()][next.getMonth()] = [];
                }
                dates[next.getFullYear()][next.getMonth()].push(next);
                last = next;
            }
            return dates;
        }
//生成左边的标题栏
        function addVtHeader(div, data, cellHeight) {
            var headerDiv = jQuery("<div>", { "class": "ganttview-vtheader" });
			//添加甘特图左边标题
			var titleDiv = jQuery("<div>", { "class": "ganttview-vtheader-title" });
            //项目编码
			titleDiv.append(jQuery("<div>", {
			    "class": "ganttview-vtheader-item-code easyui-resizable"
			}).append("项目编码"));
            //项目名称
				titleDiv.append(jQuery("<div>", {
				    "class": "ganttview-vtheader-item-name easyui-resizable"
                }).append("项目名称"));
				//管理员
				 titleDiv.append(jQuery("<div>", {
                    "class": "ganttview-vtheader-item-mgr" 
                 }).append("管理"));
				//项目描述
				 titleDiv.append(jQuery("<div>", {
				     "class": "ganttview-vtheader-item-desc easyui-resizable"
                }).append("项目描述")); 
					 
                    titleDiv.append(jQuery("<div>", { "class": "ganttview-vtheader-series-name" })
						.append("节点名称"));
						//执行人
						 titleDiv.append(jQuery("<div>", { "class": "ganttview-vtheader-series-exec" })
						.append("执行人"));
						//完成时间
							 titleDiv.append(jQuery("<div>", { "class": "ganttview-vtheader-series-time" })
						.append("完成时间"));
			
			headerDiv.append(titleDiv);
			
			
            for (var i = 0; i < data.length; i++) {
                var itemDiv = jQuery("<div>", { "class": "ganttview-vtheader-item" });
                //项目编号
                itemDiv.append(jQuery("<div>", {
                    "class": "ganttview-vtheader-item-code",
                    "css": { "height": (data[i].series.length * cellHeight) + "px" }
                }).append(data[i].code));
                //项目名称
				itemDiv.append(jQuery("<div>", {
                    "class": "ganttview-vtheader-item-name",
                    "css": { "height": (data[i].series.length * cellHeight) + "px" }
                }).append(data[i].name));
				//管理员
				 itemDiv.append(jQuery("<div>", {
                    "class": "ganttview-vtheader-item-mgr",
                    "css": { "height": (data[i].series.length * cellHeight) + "px" }
                }).append(data[i].mgr)); 
				//项目描述
				 itemDiv.append(jQuery("<div>", {
                    "class": "ganttview-vtheader-item-desc",
                    "css": { "height": (data[i].series.length * cellHeight) + "px" }
                }).append(data[i].desc));
                var seriesDiv = jQuery("<div>", { "class": "ganttview-vtheader-series" });
                //添加项目节点
                for (var j = 0; j < data[i].series.length; j++) {
                    var seriesnodeDiv = jQuery("<div>", { "class": "ganttview-vtheader-series-node" });//定义节点容器
					//为每个节点添加容器
					// seriesDiv.append(jQuery("<div>", { "class": "ganttview-vtheader-series-node" }));
					//节点名称
                    seriesnodeDiv.append(jQuery("<div>", { "class": "ganttview-vtheader-series-name" })
						.append(data[i].series[j].name));
						//执行人
                    seriesnodeDiv.append(jQuery("<div>", { "class": "ganttview-vtheader-series-exec" })
						.append(data[i].series[j].exec));
				    //完成时间,如为MM-DD-MM-DD，则格式化为MM/DD-MM/DD
						 var str = data[i].series[j].time;
						 if ((str.split('-')).length == 4) { str = str.replace(/-/, "/").replace(/(.*)-/, "$1/") }
						 seriesnodeDiv.append(jQuery("<div>", { "class": "ganttview-vtheader-series-time" })
						.append(str));
						 seriesDiv.append(seriesnodeDiv);
                }
                itemDiv.append(seriesDiv);
                headerDiv.append(itemDiv);
            }
            div.append(headerDiv);
        }
		
		
//生成顶部的标题栏
        function addHzHeader(div, dates, cellWidth) {
            var headerDiv = jQuery("<div>", { "class": "ganttview-hzheader" });
            var monthsDiv = jQuery("<div>", { "class": "ganttview-hzheader-months" });
            var daysDiv = jQuery("<div>", { "class": "ganttview-hzheader-days" });
            var totalW = 0;
			switch (opts.scale){
				case "months":
						//cellWidth=cellWidth*2;
				     for (var y in dates) {
						var i=0;
						  for (var m in dates[y]) {//每月
						  i+=1;
							  daysDiv.append(jQuery("<div>", { "class": "ganttview-hzheader-day" })
						 			.append(monthNames[m]));
						 }
						  var w = i * cellWidth;
						 totalW = totalW + w;
						 monthsDiv.append(jQuery("<div>", {
						     "class": "ganttview-hzheader-month",
						     "css": { "width": (w - 1) + "px" }
						 }).append(  y+"年"));						 
				}
					break;					
					
				case "days":	
				default:
				   for (var y in dates) {
				    for (var m in dates[y]) {
				        var w = dates[y][m].length * cellWidth;
				        totalW = totalW + w;
				        monthsDiv.append(jQuery("<div>", {
				            "class": "ganttview-hzheader-month",
				            "css": { "width": (w - 1) + "px" }
				        }).append(monthNames[m] + "/" + y));
				        for (var d in dates[y][m]) {
				            daysDiv.append(jQuery("<div>", { "class": "ganttview-hzheader-day" })
								.append(dates[y][m][d].getDate()));
				        }
				    }
				}				
					break;
			}
			
       
            monthsDiv.css("width", totalW + "px");
            daysDiv.css("width", totalW + "px");
            headerDiv.append(monthsDiv).append(daysDiv);
            div.append(headerDiv);
        }
		
//生成甘特图网格
        function addGrid(div, data, dates, cellWidth, showWeekends) {
            var gridDiv = jQuery("<div>", { "class": "ganttview-grid" });
            var rowDiv = jQuery("<div>", { "class": "ganttview-grid-row" });
			 switch (opts.scale){
				case "months":
				for (var y in dates) {
				    for (var m in dates[y]) { //按月显示
						 var cellDiv = jQuery("<div>", { "class": "ganttview-grid-row-cell" }); 
						rowDiv.append(cellDiv); 
				    }
				}
					break;					
					
				case "days":	
				default:
				for (var y in dates) {
				    for (var m in dates[y]) { 
				        for (var d in dates[y][m]) {//按天显示
				            var cellDiv = jQuery("<div>", { "class": "ganttview-grid-row-cell" });
				            if (DateUtils.isWeekend(dates[y][m][d]) && showWeekends) {
				                cellDiv.addClass("ganttview-weekend");
				            }
				            rowDiv.append(cellDiv);
				        }
				    }
				}				
					break;
			}
            var w = jQuery("div.ganttview-grid-row-cell", rowDiv).length * cellWidth;
            rowDiv.css("width", w + "px");
            gridDiv.css("width", w + "px");
            for (var i = 0; i < data.length; i++) {
                for (var j = 0; j < data[i].series.length; j++) {
                    gridDiv.append(rowDiv.clone());
                }
            }
            div.append(gridDiv);
        }
		
//生成甘特图条块容器
        function addBlockContainers(div, data) {
            var blocksDiv = jQuery("<div>", { "class": "ganttview-blocks" });
            for (var i = 0; i < data.length; i++) {
                for (var j = 0; j < data[i].series.length; j++) {
                    blocksDiv.append(jQuery("<div>", { "class": "ganttview-block-container" }));
                }
            }
            div.append(blocksDiv);
        }
//生成甘特图条块,计划时间和实际时间，0为计划
        function addBlocks(div, data, cellWidth, start) {
            var rows = jQuery("div.ganttview-blocks div.ganttview-block-container", div);
            var rowIdx = 0;
            for (var i = 0; i < data.length; i++) {
                for (var j = 0; j < data[i].series.length; j++) {
                    var series = data[i].series[j];
					//计划时间条块
					var size0 =0;//计算间隔长度；
					var offset0=0;
					var block0;
					 switch (opts.scale){
					 	case "months":
						 size0 = DateUtils.monthsBetween(series.start0, series.end0) + 1;//计算月数
						  offset0 = DateUtils.monthsBetween(start, series.start0);
						   block0 = jQuery("<div>", {
						      "class": "ganttview-block0",
						      "title": series.name + ",计划工时： " + size0 + " 月",//鼠标停留显示
						      "css": {
						          "width": ((size0 * cellWidth) - 9) + "px",
						          "margin-left": ((offset0 * cellWidth) + 3) + "px"
						      }
						  });
						  	addBlockData(block0, data[i], series);
						   if (data[i].series[j].color0) {
						      block0.css("background-color", data[i].series[j].color0);
						   }
						  block0.append(jQuery("<div>", { "class": "ganttview-block-text" }).text(size0+"月"));
						  jQuery(rows[rowIdx]).append(block0);
					 		break;
						 case "days": 
					 	default:
						 size0 = DateUtils.daysBetween(series.start0, series.end0) + 1;//计算天数
						offset0 = DateUtils.daysBetween(start, series.start0);
						block0 = jQuery("<div>", {
						    "class": "ganttview-block0",
						    "title": series.name + ",计划工时： " + size0 + " 天",//鼠标停留显示
						    "css": {
						        "width": ((size0 * cellWidth) - 9) + "px",
						        "margin-left": ((offset0 * cellWidth) + 3) + "px"
						    }
						});
							addBlockData(block0, data[i], series);
						 if (data[i].series[j].color0) {
						    block0.css("background-color", data[i].series[j].color0);
						 }
						block0.append(jQuery("<div>", { "class": "ganttview-block-text" }).text(size0+"天"));
						jQuery(rows[rowIdx]).append(block0);
					 		break;
					 }
					
					
				
					//实际时间条块
                    
					
					 switch (opts.scale){
						case "months":
											 var size = DateUtils.monthsBetween(series.start, series.end) + 1;
											 var offset = DateUtils.monthsBetween(start, series.start);
											 var block = jQuery("<div>", {
											     "class": "ganttview-block",
											     "title": series.name + ",实际工时： " + size + " 月",
											     "css": {
											         "width": ((size * cellWidth) - 9) + "px",
											         "margin-left": ((offset * cellWidth) + 3) + "px"
											     }
											 });
											 addBlockData(block, data[i], series);
											 if (data[i].series[j].color) {
											     block.css("background-color", data[i].series[j].color);
											 }
											 block.append(jQuery("<div>", { "class": "ganttview-block-text" }).text(size + "月"));
											 jQuery(rows[rowIdx]).append(block);
							break;
											 case "days": 
						default:
											 var size = DateUtils.daysBetween(series.start, series.end) + 1;
											 var offset = DateUtils.daysBetween(start, series.start);
											 var block = jQuery("<div>", {
											     "class": "ganttview-block",
											     "title": series.name + ",实际工时： " + size + " 天",
											     "css": {
											         "width": ((size * cellWidth) - 9) + "px",
											         "margin-left": ((offset * cellWidth) + 3) + "px"
											     }
											 });
											 addBlockData(block, data[i], series);
											 if (data[i].series[j].color) {
											     block.css("background-color", data[i].series[j].color);
											 }
											 block.append(jQuery("<div>", { "class": "ganttview-block-text" }).text(size + "天"));
											 jQuery(rows[rowIdx]).append(block);
							break;
					}
					
					
					
                    rowIdx = rowIdx + 1;
					
					
					
                }
            }
        }
//生成甘特图条块数据，鼠标放置能显示
        function addBlockData(block, data, series) {
            // This allows custom attributes to be added to the series data objects
            // and makes them available to the 'data' argument of click, resize, and drag handlers
            var blockData = { id: data.id, name: data.name };
            jQuery.extend(blockData, series);
            block.data("block-data", blockData);
        }

        function applyLastClass(div) {
            jQuery("div.ganttview-grid-row div.ganttview-grid-row-cell:last-child", div).addClass("last");
            jQuery("div.ganttview-hzheader-days div.ganttview-hzheader-day:last-child", div).addClass("last");
            jQuery("div.ganttview-hzheader-months div.ganttview-hzheader-month:last-child", div).addClass("last");
        }

        return {
            render: render
        };
    }

	//定义行为函数
    var Behavior = function (div, opts) {

        function apply() {

            if (opts.behavior.clickable) {
                bindBlockClick(div, opts.behavior.onClick);
            }

            if (opts.behavior.resizable) {
                bindBlockResize(div, opts.cellWidth, opts.start, opts.behavior.onResize);
            }

            if (opts.behavior.draggable) {
                bindBlockDrag(div, opts.cellWidth, opts.start, opts.behavior.onDrag);
            }
        }
//绑定点击消息函数
        function bindBlockClick(div, callback) {
            //by Ben 升级兼容新版jQuery,on(events,[selector],[data],fn)
            // jQuery("div.ganttview-block", div).live("click", function () {
				//alert("bindBlockClick");ganttview-block
				$("div.ganttview-block").on("click",function(){
					//alert("hello 绑定 on");
					//});
         //   jQuery("div.ganttview-block").on('click', 'div', function() {
                if (callback) { callback(jQuery(this).data("block-data")); }
            });
        }
//绑定调整尺寸消息函数
        function bindBlockResize(div, cellWidth, startDate, callback) {
            //jQuery("div.ganttview-block", div).resizable({
				$("div.ganttview-block").resizable({
               // grid: cellWidth,
               handles: "e,w",
                onStopResize: function () {
                    var block = jQuery(this);
                    updateDataAndPosition(div, block, cellWidth, startDate);
                    if (callback) { callback(block.data("block-data")); }
                }
            });
        }
//绑定拖拽消息函数
        function bindBlockDrag(div, cellWidth, startDate, callback) {
            jQuery("div.ganttview-block", div).draggable({
                axis: "x",
                grid: [cellWidth, cellWidth],
                stop: function () {
                    var block = jQuery(this);
                    updateDataAndPosition(div, block, cellWidth, startDate);
                    if (callback) { callback(block.data("block-data")); }
                }
            });
        }

        function updateDataAndPosition(div, block, cellWidth, startDate) {
            var container = jQuery("div.ganttview-slide-container", div);
            var scroll = container.scrollLeft();
            var offset = block.offset().left - container.offset().left - 1 + scroll;

            // Set new start date
            var daysFromStart = Math.round(offset / cellWidth);
            var newStart = startDate.clone().addDays(daysFromStart);
            block.data("block-data").start = newStart;

            // Set new end date
            var width = block.outerWidth();
            var numberOfDays = Math.round(width / cellWidth) - 1;
            block.data("block-data").end = newStart.clone().addDays(numberOfDays);
            jQuery("div.ganttview-block-text", block).text(numberOfDays + 1);

            // Remove top and left properties to avoid incorrect block positioning,
            // set position to relative to keep blocks relative to scrollbar when scrolling
            block.css("top", "").css("left", "")
				.css("position", "relative").css("margin-left", offset + "px");
        }

        return {
            apply: apply
        };
    }

    var ArrayUtils = {

        contains: function (arr, obj) {
            var has = false;
            for (var i = 0; i < arr.length; i++) { if (arr[i] == obj) { has = true; } }
            return has;
        }
    };

    var DateUtils = {

        daysBetween: function (start, end) {
            if (!start || !end) { return 0; }
            start = Date.parse(start); end = Date.parse(end);
            if (start.getYear() == 1901 || end.getYear() == 8099) { return 0; }
            var count = 0, date = start.clone();
            while (date.compareTo(end) == -1) { count = count + 1; date.addDays(1); }
            return count;
        },
		 monthsBetween: function (start, end) {
		    if (!start || !end) { return 0; }
		    start = Date.parse(start); end = Date.parse(end);
		    if (start.getYear() == 1901 || end.getYear() == 8099) { return 0; }
			var count=(end.getFullYear()-start.getFullYear())*12+end.getMonth()-end.getMonth();
		    return count;
		},

        isWeekend: function (date) {
            return date.getDay() % 6 == 0;
        },

        getBoundaryDatesFromData: function (data, minDays) {
            var minStart = new Date(); maxEnd = new Date();
            for (var i = 0; i < data.length; i++) {
                for (var j = 0; j < data[i].series.length; j++) {
                    var start = Date.parse(data[i].series[j].start);
                    var end = Date.parse(data[i].series[j].end)
                    var start0 = Date.parse(data[i].series[j].start0);
                    var end0 = Date.parse(data[i].series[j].end0)
                    if (i == 0 && j == 0) { minStart = start; maxEnd = end; }
                    if (minStart.compareTo(start) == 1) { minStart = start; }
                    if (maxEnd.compareTo(end) == -1) { maxEnd = end; }
                    if (minStart.compareTo(start0) == 1) { minStart = start0; }
                    if (maxEnd.compareTo(end0) == -1) { maxEnd = end0; }
                }
            }

            // Insure that the width of the chart is at least the slide width to avoid empty
            // whitespace to the right of the grid
            if (DateUtils.daysBetween(minStart, maxEnd) < minDays) {
                maxEnd = minStart.clone().addDays(minDays);
            }

            return [minStart, maxEnd];
        }
    };

})(jQuery);